#ifndef _COM_FACTORY_HPP_
#define _COM_FACTORY_HPP_

#include <crt_util.h>
#include <dlcom/comfunc.hpp>
#include <dlcom/comsentry.hpp>

namespace StdCom
{

class CUnknownImp
{
public:
    // volatile ULONG m_Ref;
    _atomic_t ref;
    CUnknownImp() { ref.value = 0; }
    virtual ~CUnknownImp() { ref.value = 0; }
};

typedef struct stdcom_entry_s stdcom_entry_t;

struct stdcom_entry_s {
    CLSID clsid;
    HRESULT (*pfnGetClassObject)(const IID&, void**);
    char progid[MAX_PROGIDLEN];
};

template <class T> class ClassFactory : public IClsFactory
{
public:
    ClassFactory() : ref(0) { }
    virtual ~ClassFactory()
    { /*loge("ClassFactory::~ClassFactory"); */
    }

private:
    typedef ClassFactory<T> ClsPluginCreator;
    ULONG ref;

public:
    std_method(QueryInterface)(REFIID riid, void** ppv)
    {
        if (IID_IClsFactory == riid) {
            *ppv = static_cast<IClsFactory*>(this);
            this->AddRef();
            return S_OK;
        }
        return E_NOINTERFACE;
    }
    std_method_(ULONG, AddRef)()
    {
        this->ref++;
        return this->ref;
    }
    std_method_(ULONG, Release)()
    {
        this->ref--;
        if (this->ref) {
            return this->ref;
        } else {
            delete this;
        }
        return 0;
    }

public:
    std_method_(HRESULT, CreateInstance)(IBase* pUnkOuter, REFIID riid, void** ppvObject) { return CoCreateInstance(pUnkOuter, riid, ppvObject); }
    std_method_(HRESULT, LockServer)(int fLock) { return S_OK; }

public:
    static HRESULT CoCreateInstance(IBase* punkOuter, REFIID riid, void** ppv)
    {
        *ppv = 0;

        rc_error(!punkOuter || IID_IBase == riid, E_INVALIDARG);

        SafePtr<T*> p = ALLOC_NEW T();
        rc_error(p.m_p != NULL, E_OUTOFMEMORY);

        p.m_p->AddRef();
        HRESULT hr = p.m_p->QueryInterface(riid, ppv);
        T* ptr = p.detach();
        ptr->Release();

        return hr;
    }
    static HRESULT GetClass(const IID& riid, void** ppv) { return ClassFactory<ClsPluginCreator>::CoCreateInstance(0, riid, ppv); }
};

} // namespace StdCom

#endif
